
#include "Bloque.h"

Bloque::Bloque(int size)
{
	this->sizeBloque = size;
	setCantidadBytesLibres(size);
	this->cantidadRegistros=0;
	tDispersion = 1;
}

Bloque::~Bloque()
{
	for(int i=0; i<cantidadRegistros; i++)
		delete registros[i];
}

int Bloque::getPAR(Registro* registro)
{
	for(int i=0;i<(int)registros.size();i++)
	{
		if(registros[i]->equals(registro))
			return i;
	}
	return -1;
}

void Bloque::setCantidadBytesLibres(short cantidad)
{
	this->cantidadBytesLibres = cantidad - sizeof(cantidadBytesLibres) - sizeof(cantidadRegistros) - sizeof(tDispersion);
}

void Bloque::setSize(int size)
{
	this->sizeBloque = size;
}

int Bloque::getSize()
{
	return this->sizeBloque;
}

int Bloque::getTamanioDispersion()
{
	return this->tDispersion;
}

void Bloque::setTamanioDispersion(int tamanioDispersion)
{
	this->tDispersion = tamanioDispersion;
}

short Bloque::getCantidadBytesLibres()
{
	return cantidadBytesLibres;
}

/**
 * Agrega el registro al bloque. De lo contrario devuelve false
 */
bool Bloque::agregarRegistro(Registro* registro)
{
	//Veo unicidad primero del registro en el bloque.
	for(int i=0; i<(int)registros.size(); i++)
	{
		if(registros[i]->equals(registro))
			return false;
	}

	//Controlo espacio libre
	if(registro->getLongitud()+sizeof(int) > (unsigned)this->cantidadBytesLibres)
		return false;

	Registro* nuevo = new Registro(-1);
	nuevo->clonar(registro);

	registros.push_back(nuevo);
	this->cantidadRegistros++;
	this->cantidadBytesLibres -= (nuevo->getLongitud() + sizeof(int));
	return true;
}

/*
 * Elimina del vector registros.
 */
bool Bloque::eliminarRegistro(Registro* registro)
{
	for(int i=0;i<(int)registros.size();i++)
	{
		if(registros[i]->equals(registro))
		{
			//Sumo los bytes que quedaron libres.
			this->cantidadBytesLibres += registros[i]->getLongitud()+sizeof(int);
			//Resto el numero de registros que estan en el bloque.
			this->cantidadRegistros--;
			//Libero primero la referencia.
			delete registros[i];
			//Elimino del vector de registros.
			registros.erase(registros.begin()+i);
			return true;
		}

	}
	return false;
}

/*
 * Modifica el registro de la siguiente forma:elimina el registro antiguo , compacta y agrega al final si hay lugar.
 */
bool Bloque::modificarRegistro(Registro* registro)
{
	int prb = getPAR(registro);

	if(prb != -1)
	{
		//Calculo el espacio libre que tendria el registro para actualizarse.
		int espacioLibre = cantidadBytesLibres + registros[prb]->getLongitud();

		//Vemos si el registro modificado cabe dentro del bloque
		if(registro->getLongitud()+sizeof(int) <= (unsigned)espacioLibre)
		{
			//Actualizo el registro en la posicion prb.
			registros[prb]->clonar(registro);
			cantidadBytesLibres = espacioLibre - (registro->getLongitud()+sizeof(int));
			return true;
		}
		//Aca elimina y compacta por que es un vector dinamico.
		else if(eliminarRegistro(registro)) {
			//Trata de agregar el registro al bloque.De lo contrario retorna false.
			return agregarRegistro(registro);
		}
	}
	return false;
}

bool Bloque::buscarRegistro(Registro* registro)
{
	int posicion = getPAR(registro);

	if(posicion != -1)
	{
		registro->clonar(registros[posicion]);
		return true;
	}
	else
		return false;
}

Registro* Bloque::desacolarRegistros()
{
	if(!registros.empty())
	{
		Registro* registro = new Registro(-1);
		registro->clonar(registros[0]);
		eliminarRegistro(registros[0]);
		return registro;
	}
	else
		return NULL;
}

std::vector<Registro*>* Bloque::getRegistros()
{
	return &registros;
}

char* Bloque::serializar()
{
	char* bloqueSerializado = new char[getSize()];
	int start = 0;

	//Agrego la metadata al ppio del bloque: CantRegistros, CantBytesLibres, TamDispersion
	memcpy(bloqueSerializado+start,&cantidadRegistros,sizeof(cantidadRegistros));
	start += sizeof(cantidadRegistros);

	memcpy(bloqueSerializado+start,&cantidadBytesLibres,sizeof(cantidadBytesLibres));
	start += sizeof(cantidadBytesLibres);

	memcpy(bloqueSerializado+start,&tDispersion,sizeof(tDispersion));
	start += sizeof(tDispersion);
	//Fin metadata

	//En cada iteracion llama al serializar del registro y concatena en el char* bloqueSerializado.
	for(int i=0; i<(int)registros.size(); i++)
	{
		//Guardo el tamaño del registro al principio del mismo dentro del bloque serializado.
		int tamanioRegistro = registros[i]->getLongitud();
		memcpy(bloqueSerializado+start,&tamanioRegistro,sizeof(tamanioRegistro));
		start += sizeof(tamanioRegistro);

		//Guardo el registro serializado.
		char* serializado = registros[i]->getComoArrayDeBytes();
		memcpy(bloqueSerializado+start,serializado,tamanioRegistro);
		delete[] serializado;

		start += tamanioRegistro;
	}

	return bloqueSerializado;
}

void Bloque::hidratar(char* bloque)
{
	short start = 0;

	//Hidrato la metadata
	/*********************/

	memcpy(&cantidadRegistros,bloque+start,sizeof(cantidadRegistros));
	start += sizeof(cantidadRegistros);

	memcpy(&cantidadBytesLibres,bloque+start,sizeof(cantidadBytesLibres));
	start += sizeof(cantidadBytesLibres);

	memcpy(&tDispersion,bloque+start,sizeof(tDispersion));
	start += sizeof(tDispersion);

	/********************/

	for(int i=0; i<(int)cantidadRegistros; i++)
	{
		int tamanioRegistro;
		memcpy(&tamanioRegistro,bloque+start,sizeof(tamanioRegistro));
		start += sizeof(tamanioRegistro);

		char* registroTemp = new char[tamanioRegistro];
		memcpy(registroTemp,bloque+start,tamanioRegistro);

		Registro* registro = new Registro(-1);
		registro->hidratar(registroTemp, tamanioRegistro);
		registros.push_back(registro);

		start += tamanioRegistro;

		delete[] registroTemp;
	}
}

int Bloque::getCantidadRegistros()
{
	return registros.size();
}

bool Bloque::tieneRegistros() {
	return !registros.empty();
}

bool Bloque::equals(Bloque* bloque)
{
	if(this->getCantidadRegistros() != bloque->getCantidadRegistros())
		return false;
	else
	{
		Registro registro(-1);
		for(int i=0; i<(int)registros.size(); i++)
		{
			registro.clonar(registros[i]);
			if(!bloque->buscarRegistro(&registro))
				return false;
		}
	}
	return true;
}
